﻿using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;

namespace Hsenl {
    public struct SwitchToMainThreadPoolAwaitable {
        public static SwitchToMainThreadPoolAwaitable Create() => default;

        public Awaiter GetAwaiter() => default;

        public struct Awaiter : ICriticalNotifyCompletion {
            public bool IsCompleted => false;

            public void GetResult() { }

            public void OnCompleted(Action continuation) {
                Framework.Instance.ThreadSynchronizationContext.Post(continuation);
            }

            public void UnsafeOnCompleted(Action continuation) {
                Framework.Instance.ThreadSynchronizationContext.Post(continuation);
            }
        }
    }

    public struct SwitchToThreadPoolAwaitable {
        public static SwitchToThreadPoolAwaitable Create() => default;

        public Awaiter GetAwaiter() => default;

        public struct Awaiter : ICriticalNotifyCompletion {
            public bool IsCompleted => false;

            public void GetResult() { }

            public void OnCompleted(Action continuation) {
                ThreadPool.QueueUserWorkItem(SwitchCompletedCallback, continuation);
            }

            public void UnsafeOnCompleted(Action continuation) {
                ThreadPool.UnsafeQueueUserWorkItem(SwitchCompletedCallback, continuation);
            }

            private static void SwitchCompletedCallback(object state) {
                ((Action)state!).Invoke();
            }
        }
    }

    // 如果是需要长期运行的 SwitchToThread, 则直接创建一个 HThreadTask 是更合适的选择, 而不是用上面的线程池. 既不会抢占线程池资源, await 的速度也比上面的快很多.
    public class HThreadTask {
        private readonly ConcurrentQueue<Action> _actionQueue = new();
        private readonly AutoResetEvent _queuedEvent = new(false);
        private volatile bool _running = true;

        public HThreadTask() {
            new Thread(this.WorkLoop) {
                IsBackground = true,
                Name = "HThreadTask Worker"
            }.Start();
        }

        private void WorkLoop() {
            while (this._running) {
                this._queuedEvent.WaitOne();
                while (this._actionQueue.TryDequeue(out var task)) {
                    try {
                        task.Invoke();
                    }
                    catch (Exception ex) {
                        Log.Error($"HThreadTask invoke exception: {ex}");
                    }
                }
            }
        }

        public WorkTaskAwaitable SwitchToThread() {
            return new WorkTaskAwaitable(this);
        }

        public void Stop() {
            this._running = false;
        }

        public readonly struct WorkTaskAwaitable {
            public static WorkTaskAwaitable Create(HThreadTask threadTask) => new(threadTask);
            private readonly HThreadTask _threadTask;

            public WorkTaskAwaitable(HThreadTask threadTask) {
                this._threadTask = threadTask;
            }

            public Awaiter GetAwaiter() => new(this._threadTask);

            public readonly struct Awaiter : ICriticalNotifyCompletion {
                private readonly HThreadTask _threadTask;

                public bool IsCompleted => false;

                public Awaiter(HThreadTask threadTask) {
                    this._threadTask = threadTask;
                }

                public void GetResult() { }

                public void OnCompleted(Action continuation) {
                    this._threadTask._actionQueue.Enqueue(continuation);
                    this._threadTask._queuedEvent.Set();
                }

                public void UnsafeOnCompleted(Action continuation) {
                    this._threadTask._actionQueue.Enqueue(continuation);
                    this._threadTask._queuedEvent.Set();
                }
            }
        }
    }
}